//select用于监听多个文件描述符

#pragma once
#include <iostream>
#include <string>
#include "Socket.hpp"
#include "Common.hpp"
#include <memory>
#include "Log.hpp"
#include <sys/epoll.h>

using namespace SocketModule;
using namespace LogModule;



class EpollServer
{
    static const int revs_num = 64;
public:
    EpollServer(int port)//给监听套接字的port
    :_port(port)
    ,_listen_socket(std::make_unique<TcpSocket>())
    ,_isrunning(false)
    ,_epfd(gdefaultsockfd)
    {}

    void Init()
    {
        _listen_socket->BuildTcpSocketMethod(_port);

        _epfd=epoll_create(256);
        if(_epfd < 0)
        {
            LOG(LogLevel::ERROR)<< "epoll_create error";
            exit(EPOLL_CREATE_ERR);
        }
        LOG(LogLevel::DEBUG) << "epoll create success: "<<_epfd;

        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listen_socket->Fd();

        int n = epoll_ctl(_epfd,EPOLL_CTL_ADD,_listen_socket->Fd(),&ev);
        if(n < 0)
        {
            LOG(LogLevel::ERROR)<<"epoll_ctl error ";
            exit(EPOLL_CTL_ERR);
        }

    }

    void Loop()
    {
        int timeout=-1;
        _isrunning=true;
       
        while(_isrunning)
        {
            int n=epoll_wait(_epfd,_revs, revs_num,timeout);
            switch(n)
            {
            case 0:
                std::cout<<"timeout"<<std::endl;
                break;
            case -1:
                std::cout<<"epoll_wait error"<<std::endl;
                break;
            default:
                std::cout<<"有事件就绪了"<<std::endl;
                Dispatcher(n);
                break;
                

            }
        }

        _isrunning=false;


    }

    void Dispatcher(int n)//将listen套接字文件描述符和其余普通套接字一起交由select监听，再分发
    {
        for(int i=0;i<n;i++)
        {
            int event =_revs[i].events;
            int fd = _revs[i].data.fd;
            if(fd == _listen_socket->Fd())
            {
                if(event & EPOLLIN)
                {
                    Accept();
                }
            }
            else
            {
                if(event & EPOLLIN)
                {
                    Recver(fd);
                }
            }

        }

    }

    void Accept()
    {
        InetAddr client;
        int newfd= _listen_socket->Accepter(&client);

        if(newfd<0)
        {
            return ;
        }
        else
        {
            std::cout << "获得了一个新的链接: " << newfd << " client info: " << client.Addr() << std::endl;
            //添加节点是往红黑树加，辅助数组是用来获取就绪fd
            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = newfd;
            int n=epoll_ctl(_epfd,EPOLL_CTL_ADD,newfd,&ev);
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                close(newfd);
            }
            LOG(LogLevel::DEBUG)<< "epoll_ctl success: "<<newfd;
        }
    }

    void Recver(int fd)
    {
        char buffer[1024];
        ssize_t n=recv(fd,buffer,sizeof(buffer)-1,0);
        if(n==0)
        {
            std::cout<<"tcp 断开"<<std::endl;
            int m=epoll_ctl(_epfd,EPOLL_CTL_DEL, fd, nullptr);
            if(m<0)
            {
                LOG(LogLevel::ERROR)<<"epoll_ctl fail";
                return;
            }
            LOG(LogLevel::DEBUG) << "epoll_ctl success: " << fd;
            close(fd);
            
        }
        else if(n>0)
        {
            buffer[n]=0;
            std::string s="echo# ";
            s+=buffer;
            ::send(fd,s.c_str(),s.size(),0);
        }
        else
        {
            std::cout<<"recv error"<<std::endl;
            int m=epoll_ctl(_epfd,EPOLL_CTL_DEL, fd, nullptr);
            if(m<0)
            {
                LOG(LogLevel::ERROR)<<"epoll_ctl fail";
                return;
            }
            LOG(LogLevel::DEBUG) << "epoll_ctl success: " << fd;
            close(fd);

        }

    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    bool _isrunning;
    int _epfd;//epoll文件描述符
    struct epoll_event _revs[revs_num];//从就绪队列拷贝到_revs



};